[Unity 3d] 实现黑夜/白天一键切换全攻略

您所在的位置:网站首页 unity 怎么烘焙 [Unity 3d] 实现黑夜/白天一键切换全攻略

[Unity 3d] 实现黑夜/白天一键切换全攻略

2023-09-27 06:06| 来源: 网络整理| 查看: 265

在本文,笔者讲简单的讲一讲怎么在 Unity 中实现黑夜白天的一键切换。

演示:

场景中的灯效,除了直射光其他均是烘焙出来的。

色彩太丰富,实在不敢录太长时间 原理:

如果想要实现上面的这个黑夜白天切换效果,控制好以下几点就行了:

保证烘焙黑夜和白天光照贴图使用的是同一个场景(Ctrl+D挺好用)。 动态切换 黑夜和白天的 光照贴图设置(LightmapSettings)。 动态修改天空盒白天和黑夜对应的天空盒,或者像上面演示的那样:仅仅修改天空盒曝光值。 修改直射光的强度(如果有必要,可以修改直射光的角度)。 如果像演示中的镜面建筑,加了光照探针(ReflectionProbe)的需要指定对应的烘焙OK的贴图。 关于黑夜白天差异性的游戏对象: 不参与烘焙的,比如上面的效果晚上多加的 Lens 辉光,直接白天显示隐藏晚上显示即可。 参与烘焙的,可以直接使用显示 / 隐藏操作。 参与烘焙的,也可以存为预制体动态加载,但该方案要写点代码保证游戏对象上烘焙的光照信息能被加载出来。 示例: using System.Linq; using UnityEngine; using DG.Tweening; public class TestSwitchBakedLightMap : MonoBehaviour { [Header("Common Configuration and Component ")] public ReflectionProbe reflectionProbe; public Light directLight; public GameObject effectLights; //晚上要打灯,白天不打灯 [Header("Configuration and Component For Day"), Space(10)] public LightDataConfiguration dayConfig; public Cubemap dayReflect; public float daySkyboxExposure = 1f; public float directLightDayIntensity = 1; public float dayLightIntensityMultiplier = 1; [Header("Configuration and Component For Night"), Space(10)] public LightDataConfiguration nightConfig; public Cubemap nightReflect; public float nightSkyboxExposure = 0.2f; public float directLightNightIntensity = 0.2f; public float nightLightIntensityMultiplier = 0.3f; // private fields LightmapData[] day; LightmapData[] night; bool isDay = true; private void Start() { night = nightConfig.Lightmaps .Select(x => new LightmapData() { lightmapColor = x.lightmapColor, shadowMask = x.shadowMask }) .ToArray(); day = dayConfig.Lightmaps .Select(x => new LightmapData() { lightmapColor = x.lightmapColor, shadowMask = x.shadowMask }) .ToArray(); RenderSettings.skybox.SetFloat("_Exposure", daySkyboxExposure); reflectionProbe.customBakedTexture = dayReflect; } void OnGUI() { if (GUILayout.Button(isDay ? "To Night" : "To Day")) { SwithDayAndNight(); } } public void SwithDayAndNight() { if (!isDay) { // 切换为白天的配置 LightmapSettings.lightmaps = day; directLight.DOIntensity(directLightDayIntensity, 0.5f); DOTween.To(()=> RenderSettings.ambientIntensity, y=> { RenderSettings.ambientIntensity = y; },dayLightIntensityMultiplier,0.5f); RenderSettings.skybox.DOFloat(daySkyboxExposure, "_Exposure", 0.5f); effectLights.SetActive(false); reflectionProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Custom; reflectionProbe.customBakedTexture = dayReflect; } else { // 切换为晚上的配置 LightmapSettings.lightmaps = night; directLight.DOIntensity(directLightNightIntensity, 0.5f); DOTween.To(()=> RenderSettings.ambientIntensity, y=> { RenderSettings.ambientIntensity = y; },nightLightIntensityMultiplier,0.5f); RenderSettings.skybox.DOFloat(nightSkyboxExposure, "_Exposure", 0.5f); effectLights.SetActive(true); reflectionProbe.customBakedTexture = nightReflect; } isDay = !isDay; } }

Tips:

仅仅是将原理中提到的几点找到对应的 API 通过代码动态修改罢了,仅供熟悉API. 使用了 DoTween 的非扩展方法形式的缓动,可以熟悉下。 使用了 DoTween 的扩展方法形式对天空盒(材质球)的曝光值进行了缓动。 使用了 ScriptableObject 分别保存的黑夜/白天的光照贴图设置信息,只需要 lightmapColor 和 shadowMask 两组信息,使用 ScriptableObject 在本示例中的好处可要好好体会体会哈~ 演示中没有出现原理中第六条第三点提到的问题,所以示例代码没做演示。 扩展阅读:

Unity5.x场景优化之动态设置光照贴图lightmap - yuyingwin的专栏 - CSDN博客 ↑怎么让动态加载的游戏对象实例化后自动加载光照贴图效果:↑

[Unity3D]Lightmapping使用及动态加载lightmap方案 - bread's code - CSDN博客 ↑更多关于光照贴图+光照探针的参考资料↑

转载请注明出处,谢谢! LightDataConfigurationEditor using Malee; using System; using UnityEngine; using System.Linq; #if UNITY_EDITOR using UnityEditor; [CustomEditor(typeof(LightDataConfiguration))] public class LightDataConfigurationEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); if (GUILayout.Button("StoreCurrentLightData")) { (target as LightDataConfiguration).StoreCurrentLightmaps(); EditorUtility.SetDirty(target); } } } #endif /// /// 抓取当前的灯光贴图 /// [CreateAssetMenu(fileName = "LightDataConfiguration", menuName = "Configration/LightDataConfiguration")] public class LightDataConfiguration : ScriptableObject { [Reorderable, SerializeField] public LightMapInfoArray Lightmaps; public string label = "Solid"; public bool forceOverride = false; public void StoreCurrentLightmaps() { if (string.IsNullOrEmpty(label)) { throw new InvalidOperationException("非法操作:未指定标签!"); } LightMapInfo info = Lightmaps.Find(v => v.name == label); if (null != info && !forceOverride) { throw new InvalidOperationException($"非法操作:已存在的条目 {label},强制覆盖请勾选 ForceOverride"); } if (null == info) { info = new LightMapInfo { name = label, Lightmaps = new LightMapPairArray() }; Lightmaps.Add(info); } else { info.Lightmaps.Clear(); } foreach (var item in LightmapSettings.lightmaps) { info.Lightmaps.Add(new LightMapPair { lightmapColor = item.lightmapColor, shadowMask = item.shadowMask }); } Debug.Log($"数据装载完毕,共计:{LightmapSettings.lightmaps.Length}个"); } public void Apply(string name) { LightMapInfo info = Lightmaps.Find(v => v.name == name); if (null == info ) { throw new InvalidOperationException($"非法操作:不存在此数据 {name}!"); } LightmapSettings.lightmaps = info.Lightmaps .Select(v=>new LightmapData { lightmapColor=v.lightmapColor, shadowMask = v.shadowMask }) .ToArray(); } public void Apply(int index) { if (index=Lightmaps.Count) { throw new InvalidOperationException($"非法操作:指定的索引超 {index} 下标!"); } LightmapSettings.lightmaps = Lightmaps[index].Lightmaps .Select(v => new LightmapData { lightmapColor = v.lightmapColor, shadowMask = v.shadowMask }) .ToArray(); } [System.Serializable] public class LightMapInfoArray : ReorderableArray { } [System.Serializable] public class LightMapPairArray : ReorderableArray { } [System.Serializable] public class LightMapInfo { public string name; [Reorderable, SerializeField] public LightMapPairArray Lightmaps; public void Clean() { Lightmaps.Clear(); name = string.Empty; } } [System.Serializable] public class LightMapPair { public Texture2D lightmapColor; public Texture2D shadowMask; } }

tips: 使用了这个插件Unity-Reorderable-List



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3